using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
using System.Net.Http.Json;
using System.Text.Json;
using System.Web;

namespace SeataNet.Core.Discovery.Nacos
{
    /// <summary>
    /// Nacos服务发现实现
    /// </summary>
    public class NacosServiceDiscovery : IServiceDiscovery, IDisposable
    {
        private readonly ILogger<NacosServiceDiscovery> _logger;
        private readonly NacosConfiguration _configuration;
        private readonly HttpClient _httpClient;
        private readonly ConcurrentDictionary<string, List<ServiceInstance>> _serviceCache;
        private readonly ConcurrentDictionary<string, Action<List<ServiceInstance>>> _subscribers;
        private readonly ConcurrentDictionary<string, Timer> _heartbeatTimers;
        private readonly SemaphoreSlim _initLock;
        private string? _accessToken;
        private DateTime _tokenExpireTime;
        private bool _disposed;

        public NacosServiceDiscovery(
            ILogger<NacosServiceDiscovery> logger,
            IOptions<NacosConfiguration> configuration,
            IHttpClientFactory httpClientFactory)
        {
            _logger = logger;
            _configuration = configuration.Value;
            _httpClient = httpClientFactory.CreateClient("NacosClient");
            _serviceCache = new ConcurrentDictionary<string, List<ServiceInstance>>();
            _subscribers = new ConcurrentDictionary<string, Action<List<ServiceInstance>>>();
            _heartbeatTimers = new ConcurrentDictionary<string, Timer>();
            _initLock = new SemaphoreSlim(1, 1);

            ConfigureHttpClient();
        }

        private void ConfigureHttpClient()
        {
            _httpClient.Timeout = TimeSpan.FromMilliseconds(_configuration.RequestTimeout);
        }

        public async Task<List<ServiceInstance>> GetInstancesAsync(string serviceName)
        {
            try
            {
                await EnsureAuthenticatedAsync();

                var serverAddress = GetAvailableServer();
                var url = $"{serverAddress}/nacos/v1/ns/instance/list?" +
                          $"serviceName={HttpUtility.UrlEncode(serviceName)}&" +
                          $"namespaceId={HttpUtility.UrlEncode(_configuration.Namespace)}&" +
                          $"groupName={HttpUtility.UrlEncode(_configuration.GroupName)}&" +
                          $"clusters={HttpUtility.UrlEncode(_configuration.ClusterName)}";

                if (!string.IsNullOrEmpty(_accessToken))
                {
                    url += $"&accessToken={HttpUtility.UrlEncode(_accessToken)}";
                }

                var response = await _httpClient.GetAsync(url);
                response.EnsureSuccessStatusCode();

                var content = await response.Content.ReadAsStringAsync();
                var result = JsonSerializer.Deserialize<NacosInstanceListResponse>(content);

                if (result == null || result.Hosts == null)
                {
                    _logger.LogWarning($"未找到服务实例: {serviceName}");
                    return new List<ServiceInstance>();
                }

                var instances = result.Hosts
                    .Where(h => h.Enabled && h.Healthy)
                    .Select(h => new ServiceInstance
                    {
                        ServiceName = serviceName,
                        InstanceId = h.InstanceId ?? $"{h.Ip}:{h.Port}",
                        Ip = h.Ip,
                        Port = h.Port,
                        Weight = h.Weight,
                        Healthy = h.Healthy,
                        Enabled = h.Enabled,
                        ClusterName = h.ClusterName ?? _configuration.ClusterName,
                        Metadata = h.Metadata ?? new Dictionary<string, string>()
                    })
                    .ToList();

                _serviceCache[serviceName] = instances;
                _logger.LogDebug($"获取服务实例成功: {serviceName}, 数量: {instances.Count}");

                return instances;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"获取服务实例失败: {serviceName}");

                // 返回缓存的实例（如果有）
                if (_serviceCache.TryGetValue(serviceName, out var cachedInstances))
                {
                    _logger.LogWarning($"使用缓存的服务实例: {serviceName}, 数量: {cachedInstances.Count}");
                    return cachedInstances;
                }

                return new List<ServiceInstance>();
            }
        }

        public async Task RegisterInstanceAsync(ServiceInstance serviceInstance)
        {
            try
            {
                await EnsureAuthenticatedAsync();

                var serverAddress = GetAvailableServer();
                var url = $"{serverAddress}/nacos/v1/ns/instance";

                var parameters = new Dictionary<string, string>
                {
                    { "serviceName", serviceInstance.ServiceName },
                    { "ip", serviceInstance.Ip },
                    { "port", serviceInstance.Port.ToString() },
                    { "namespaceId", _configuration.Namespace },
                    { "groupName", _configuration.GroupName },
                    { "clusterName", serviceInstance.ClusterName },
                    { "weight", serviceInstance.Weight.ToString() },
                    { "enabled", serviceInstance.Enabled.ToString().ToLower() },
                    { "healthy", serviceInstance.Healthy.ToString().ToLower() },
                    { "ephemeral", _configuration.Ephemeral.ToString().ToLower() }
                };

                if (!string.IsNullOrEmpty(_accessToken))
                {
                    parameters["accessToken"] = _accessToken;
                }

                // 添加元数据
                if (serviceInstance.Metadata.Any())
                {
                    parameters["metadata"] = JsonSerializer.Serialize(serviceInstance.Metadata);
                }

                var content = new FormUrlEncodedContent(parameters);
                var response = await _httpClient.PostAsync(url, content);
                response.EnsureSuccessStatusCode();

                _logger.LogInformation($"注册服务实例成功: {serviceInstance.ServiceName} - {serviceInstance.GetAddress()}");

                // 启动心跳
                StartHeartbeat(serviceInstance);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"注册服务实例失败: {serviceInstance.ServiceName} - {serviceInstance.GetAddress()}");
                throw;
            }
        }

        public async Task DeregisterInstanceAsync(ServiceInstance serviceInstance)
        {
            try
            {
                // 停止心跳
                StopHeartbeat(serviceInstance);

                await EnsureAuthenticatedAsync();

                var serverAddress = GetAvailableServer();
                var url = $"{serverAddress}/nacos/v1/ns/instance?" +
                          $"serviceName={HttpUtility.UrlEncode(serviceInstance.ServiceName)}&" +
                          $"ip={HttpUtility.UrlEncode(serviceInstance.Ip)}&" +
                          $"port={serviceInstance.Port}&" +
                          $"namespaceId={HttpUtility.UrlEncode(_configuration.Namespace)}&" +
                          $"groupName={HttpUtility.UrlEncode(_configuration.GroupName)}&" +
                          $"clusterName={HttpUtility.UrlEncode(serviceInstance.ClusterName)}";

                if (!string.IsNullOrEmpty(_accessToken))
                {
                    url += $"&accessToken={HttpUtility.UrlEncode(_accessToken)}";
                }

                var response = await _httpClient.DeleteAsync(url);
                response.EnsureSuccessStatusCode();

                _logger.LogInformation($"注销服务实例成功: {serviceInstance.ServiceName} - {serviceInstance.GetAddress()}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"注销服务实例失败: {serviceInstance.ServiceName} - {serviceInstance.GetAddress()}");
                throw;
            }
        }

        public async Task SendHeartbeatAsync(ServiceInstance serviceInstance)
        {
            try
            {
                await EnsureAuthenticatedAsync();

                var serverAddress = GetAvailableServer();
                var url = $"{serverAddress}/nacos/v1/ns/instance/beat";

                var beat = new
                {
                    serviceName = serviceInstance.ServiceName,
                    ip = serviceInstance.Ip,
                    port = serviceInstance.Port,
                    cluster = serviceInstance.ClusterName,
                    weight = serviceInstance.Weight,
                    metadata = serviceInstance.Metadata
                };

                var parameters = new Dictionary<string, string>
                {
                    { "serviceName", serviceInstance.ServiceName },
                    { "namespaceId", _configuration.Namespace },
                    { "groupName", _configuration.GroupName },
                    { "beat", JsonSerializer.Serialize(beat) }
                };

                if (!string.IsNullOrEmpty(_accessToken))
                {
                    parameters["accessToken"] = _accessToken;
                }

                var content = new FormUrlEncodedContent(parameters);
                var response = await _httpClient.PutAsync(url, content);
                response.EnsureSuccessStatusCode();

                _logger.LogTrace($"发送心跳成功: {serviceInstance.ServiceName} - {serviceInstance.GetAddress()}");
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, $"发送心跳失败: {serviceInstance.ServiceName} - {serviceInstance.GetAddress()}");
            }
        }

        public async Task SubscribeAsync(string serviceName, Action<List<ServiceInstance>> callback)
        {
            _subscribers[serviceName] = callback;

            // 立即获取一次实例列表
            var instances = await GetInstancesAsync(serviceName);
            callback(instances);

            // 启动定期轮询（简化版本，实际可以使用Nacos的UDP推送）
            _ = Task.Run(async () => await PollServiceChangesAsync(serviceName));

            _logger.LogInformation($"订阅服务成功: {serviceName}");
        }

        public Task UnsubscribeAsync(string serviceName)
        {
            _subscribers.TryRemove(serviceName, out _);
            _logger.LogInformation($"取消订阅服务: {serviceName}");
            return Task.CompletedTask;
        }

        private async Task PollServiceChangesAsync(string serviceName)
        {
            while (_subscribers.ContainsKey(serviceName) && !_disposed)
            {
                try
                {
                    await Task.Delay(5000); // 每5秒轮询一次

                    var instances = await GetInstancesAsync(serviceName);

                    if (_subscribers.TryGetValue(serviceName, out var callback))
                    {
                        callback(instances);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"轮询服务变更失败: {serviceName}");
                }
            }
        }

        private void StartHeartbeat(ServiceInstance serviceInstance)
        {
            var timerKey = $"{serviceInstance.ServiceName}:{serviceInstance.GetAddress()}";

            var timer = new Timer(
                async _ => await SendHeartbeatAsync(serviceInstance),
                null,
                TimeSpan.FromMilliseconds(_configuration.HeartbeatInterval),
                TimeSpan.FromMilliseconds(_configuration.HeartbeatInterval));

            _heartbeatTimers[timerKey] = timer;

            _logger.LogDebug($"启动心跳定时器: {timerKey}");
        }

        private void StopHeartbeat(ServiceInstance serviceInstance)
        {
            var timerKey = $"{serviceInstance.ServiceName}:{serviceInstance.GetAddress()}";

            if (_heartbeatTimers.TryRemove(timerKey, out var timer))
            {
                timer.Dispose();
                _logger.LogDebug($"停止心跳定时器: {timerKey}");
            }
        }

        private async Task EnsureAuthenticatedAsync()
        {
            // 如果没有配置用户名密码，则不需要认证
            if (string.IsNullOrEmpty(_configuration.Username) || string.IsNullOrEmpty(_configuration.Password))
            {
                return;
            }

            // 如果token还有效，直接返回
            if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpireTime)
            {
                return;
            }

            await _initLock.WaitAsync();
            try
            {
                // 双重检查
                if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpireTime)
                {
                    return;
                }

                var serverAddress = GetAvailableServer();
                var url = $"{serverAddress}/nacos/v1/auth/login";

                var parameters = new Dictionary<string, string>
                {
                    { "username", _configuration.Username },
                    { "password", _configuration.Password }
                };

                var content = new FormUrlEncodedContent(parameters);
                var response = await _httpClient.PostAsync(url, content);
                response.EnsureSuccessStatusCode();

                var result = await response.Content.ReadFromJsonAsync<NacosLoginResponse>();

                if (result != null && !string.IsNullOrEmpty(result.AccessToken))
                {
                    _accessToken = result.AccessToken;
                    _tokenExpireTime = DateTime.Now.AddSeconds(result.TokenTtl - 60); // 提前1分钟刷新
                    _logger.LogInformation("Nacos认证成功");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Nacos认证失败");
                throw;
            }
            finally
            {
                _initLock.Release();
            }
        }

        private string GetAvailableServer()
        {
            // 简化版本：返回第一个服务器
            // 实际可以实现轮询或健康检查
            return _configuration.ServerAddresses.FirstOrDefault()
                   ?? throw new InvalidOperationException("未配置Nacos服务器地址");
        }

        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;

            // 停止所有心跳
            foreach (var timer in _heartbeatTimers.Values)
            {
                timer.Dispose();
            }
            _heartbeatTimers.Clear();

            _initLock.Dispose();
            _httpClient.Dispose();
        }
    }

    #region Response Models

    internal class NacosInstanceListResponse
    {
        public string? Name { get; set; }
        public string? GroupName { get; set; }
        public string? Clusters { get; set; }
        public int CacheMillis { get; set; }
        public List<NacosHost>? Hosts { get; set; }
        public long LastRefTime { get; set; }
        public string? Checksum { get; set; }
        public bool AllIPs { get; set; }
        public bool ReachProtectionThreshold { get; set; }
        public bool Valid { get; set; }
    }

    internal class NacosHost
    {
        public string Ip { get; set; } = string.Empty;
        public int Port { get; set; }
        public bool Valid { get; set; }
        public bool Healthy { get; set; }
        public bool Marked { get; set; }
        public string? InstanceId { get; set; }
        public Dictionary<string, string>? Metadata { get; set; }
        public bool Enabled { get; set; }
        public double Weight { get; set; }
        public string? ClusterName { get; set; }
        public string? ServiceName { get; set; }
        public bool Ephemeral { get; set; }
    }

    internal class NacosLoginResponse
    {
        public string AccessToken { get; set; } = string.Empty;
        public int TokenTtl { get; set; }
        public bool GlobalAdmin { get; set; }
    }

    #endregion
}
